package com.alstudio.utils.android.net.download;

import java.io.BufferedInputStream;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.io.RandomAccessFile;
import java.io.UnsupportedEncodingException;
import java.net.URL;
import java.util.UUID;

import org.apache.http.Header;
import org.apache.http.HttpResponse;
import org.apache.http.client.methods.HttpGet;

import android.content.Context;
import android.net.http.AndroidHttpClient;
import android.os.AsyncTask;
import android.text.TextUtils;

import com.alstudio.utils.android.ALSdcardManager;
import com.alstudio.utils.android.AndroidUtils;
import com.alstudio.utils.formater.numeric.ALNumericUtils;
import com.alstudio.utils.log.ALLog;

public class ALHttpDownloadTask extends AsyncTask<Void, Integer, Long> {


	private ALHttpDownloadListener mListener;
	private final String TAG = "ALHttpDownloadTask";

	private URL URL;
	private File file;
	private String path;
	private String fileName;
	private String url;
	private Throwable exception;
	private RandomAccessFile outputStream;
	private Context mContext;
	private AndroidHttpClient mHttpClient = null;
	private String token;

	private long downloadSize;
	private long previousFileSize;
	private long totalSize;
	private int downloadPercent;
	private long networkSpeed; // 网速
	private long previousTime;
	private long totalTime;
	private String fileId;

	private String filePath;

	private boolean interrupt = false;
	private ALHttpDownloadErrorCode httpErrorStausCode = ALHttpDownloadErrorCode.AL_HTTP_DOWNLOAD_TYPE_ERROR_UNKONW;

	private final int AL_HTTP_TIME_OUT = 30000;
	private final int BUFFER_SIZE = 1024 * 8;
	private final String USER_AGENT = "ALHttpDownloadManager";

	private final String HTTP_CONTENT_DISPOSITION = "Content-Disposition";

	private final class ALProgressReportingRandomAccessFile extends
			RandomAccessFile {
		private int progress = 0;

		public ALProgressReportingRandomAccessFile(File file, String mode)
				throws FileNotFoundException {
			super(file, mode);
		}

		@Override
		public void write(byte[] buffer, int offset, int count)
				throws IOException {
			super.write(buffer, offset, count);
			progress += count;
			publishProgress(progress);
		}
	}

	public ALHttpDownloadTask(Context context, String url, String path,
			ALHttpDownloadListener listener) {
		super();
		this.path = path;
		mContext = context;
		this.url = url;
		mListener = listener;
	}

	public ALHttpDownloadTask(Context context, String url, String path,
			String fileName, ALHttpDownloadListener listener) {
		super();
		this.path = path;
		mContext = context;
		this.url = url;
		mListener = listener;
		this.fileName = fileName;
	}

	public void setDownloadStateListener(ALHttpDownloadListener listener) {
		this.mListener = listener;
	}

	public ALHttpDownloadListener getDownloadStateListener() {
		return this.mListener;
	}
	
	public void setToken(String token) {
		this.token = token;
	}
	
	public String getToken() {
		return token;
	}

	public void setFileId(String fileId) {
		this.fileId = fileId;
	}
	
	public String getFileId() {
		return fileId;
	}
	
	public String getUrl() {
		return url;
	}

	public int getDownloadPercent() {
		return downloadPercent;
	}

	public long getDownloadSize() {
		return downloadSize + previousFileSize;
	}

	public long getTotalSize() {
		return totalSize;
	}

	public long getDownloadSpeed() {
		return this.networkSpeed;
	}

	public long getTotalTime() {
		return this.totalTime;
	}

	public String getFilePath() {
		return this.filePath;
	}

	public String getFileName() {
		return this.fileName;
	}

	@Override
	protected void onPreExecute() {
		previousTime = System.currentTimeMillis();
		if (mListener != null)
			mListener.onPreDownload(fileName);
	}

	@Override
	protected Long doInBackground(Void... params) {
		try {
			return startDownload();
		} catch (Exception e) {
			if (mHttpClient != null) {
				mHttpClient.close();
			}
			exception = e;
			e.printStackTrace();
			return null;
		}
	}

	@Override
	protected void onProgressUpdate(Integer... progress) {
		if (progress.length > 1) {
			totalSize = progress[1];
			if (totalSize == -1) {
				if (mListener != null)
					mListener
							.onDownloadFail(
									ALHttpDownloadErrorCode.AL_HTTP_DOWNLOAD_TYPE_ERROR_UNKONW,
									fileName,this);
			} else {

			}
		} else {
			totalTime = System.currentTimeMillis() - previousTime;
			downloadSize = progress[0];
			downloadPercent = (int) ((downloadSize + previousFileSize) * 100 / totalSize);
			networkSpeed = downloadSize / totalTime;
			if (mListener != null)
				mListener.onUpdateProcess(this);
		}
	}

	@Override
	protected void onPostExecute(Long result) {
		if (interrupt) {
			if (mListener != null)
				mListener.onDownloadFail(httpErrorStausCode, fileName,this);
			return;
		}

		if (exception != null) {
			if (mListener != null)
				mListener.onDownloadFail(httpErrorStausCode, fileName,this);
		} else {
			if (mListener != null)
				mListener.onDownloadFinish(this);
		}
	}

	@Override
	public void onCancelled() {
		super.onCancelled();
		interrupt = true;
	}

	private long startDownload() throws Exception {

		mHttpClient = AndroidHttpClient.newInstance(USER_AGENT);
		ALLog.d("下载链接 " + url);
		HttpGet httpGet = new HttpGet(url);
		if(!TextUtils.isEmpty(token)){
			httpGet.setHeader("token",token);
		}
		
		
		HttpResponse response = mHttpClient.execute(httpGet);
		// 得到文件总大小
		totalSize = response.getEntity().getContentLength();
		if(totalSize == -1){
			
			Header header = response.getFirstHeader("filesize");
			ALLog.d("初始文件大小是多少 " + header.getValue());
			if(header != null){
				String size = header.getValue(); 
				if(!TextUtils.isEmpty(size)){
					totalSize = ALNumericUtils.parseLong(size, -1);
					ALLog.d("文件大小是多少 " + size);
				}
			}
		}

		file = new File(path);
		if (!file.exists()) {
			file.mkdirs();
		}

		if (!TextUtils.isEmpty(fileName)) {
			// 指定文件名
			file = new File(path, fileName);
		} else {

			// 没有指定文件名
			String tmpFileName = null;

			if (response.containsHeader(HTTP_CONTENT_DISPOSITION)) {
				tmpFileName = getFileNameFromHeader(response.getFirstHeader(
						HTTP_CONTENT_DISPOSITION).getValue());
			}
			if (TextUtils.isEmpty(tmpFileName)) {
				tmpFileName = getFileNameFromUrl(url);
			}

			if (TextUtils.isEmpty(tmpFileName)) {
				// 名字都没取到，用当前时间生成一个
				tmpFileName = System.currentTimeMillis() + "";
			}

			file = new File(path, tmpFileName);
			fileName = tmpFileName;
			ALLog.d( "文件地址: " + file.getAbsolutePath());
		}

		filePath = file.getAbsolutePath();
		
		File parentPath1 = file.getParentFile();

		if (!parentPath1.exists()) {
			parentPath1.mkdirs();
		}

		if (file.exists() && totalSize == file.length()) {
			// 已经下载完成，不需要重新下载了
			ALLog.d( "file already exists. Skipping download");
			mHttpClient.close();
			mHttpClient = null;
			return 0l;
		}

		// if (response.containsHeader("Accept-Ranges")) {
		// // 支持断点续传
		// // 判断是否需要断点续传
		// ALLog.d("该链接支持断点续传");
		// if (file.length() > 0 && totalSize > 0 && totalSize > file.length())
		// {
		// // 需要断点续传
		// httpGet.addHeader("Range", "bytes=" + file.length() + "-");
		// previousFileSize = file.length();
		//
		// mHttpClient.close();
		// mHttpClient = AndroidHttpClient.newInstance(USER_AGENT);
		// response = mHttpClient.execute(httpGet);
		// ALLog.d("File length:" + file.length() + " totalSize:"
		// + totalSize
		// + "\nFile is not complete, resume download now...");
		//
		// }
		// } else {
		// // 不支持断点续传，直接把文件删除重建
		// ALLog.d("该链接不支持断点续传，删除本地数据重建");
		// // file.deleteOnExit();
		// file.delete();
		// }
		// 不支持断点续传啦
		file.delete();

		// 判断是否SD卡是否还有足够空间
		long storage = ALSdcardManager.getAvailableStorage();
		ALLog.d( "ext storage:" + storage + " total size:" + totalSize);

		if (totalSize - file.length() > storage) {
			httpErrorStausCode = ALHttpDownloadErrorCode.AL_HTTP_DOWNLOAD_TYPE_ERROR_EXTENAL_STORAGE_OUT_OF_RANGE;
			interrupt = true;
			mHttpClient.close();
			// 没有足够的空间啦！
			return 0l;
		}

		try {
			outputStream = new ALProgressReportingRandomAccessFile(file, "rw");
		} catch (FileNotFoundException e) {
			httpErrorStausCode = ALHttpDownloadErrorCode.AL_HTTP_DOWNLOAD_TYPE_ERROR_FILE_NOT_FOUND;
			interrupt = true;
			mHttpClient.close();
			ALLog.d( "FileNotFoundException: " + e.toString());
			return 0l;
		}

		publishProgress(0, (int) totalSize);

		InputStream input = null;
		try {
			input = response.getEntity().getContent();
		} catch (IOException ex) {
			httpErrorStausCode = ALHttpDownloadErrorCode.AL_HTTP_DOWNLOAD_TYPE_ERROR_IO_EXCEPTION;
			interrupt = true;
			mHttpClient.close();
			ALLog.d( "get inputstream error: " + ex.toString());
			return 0l;
		}

		int bytesCopied = 0;

		try {
			bytesCopied = copy(input, outputStream);
		} catch (Exception e) {
			// TODO Auto-generated catch block
			// 拷贝数据的时候出现异常

			// 关闭输入流
			if (input != null) {
				input.close();
			}
			// 关闭输出
			if (outputStream != null) {
				outputStream.close();
			}

			mHttpClient.close();
			mHttpClient = null;

			return 0l;
		}

		if ((previousFileSize + bytesCopied) != totalSize && totalSize != -1
				&& !interrupt) {
			throw new IOException("download incomplete: " + bytesCopied
					+ " != " + totalSize);
		}

		outputStream.close();
		mHttpClient.close();
		mHttpClient = null;
		ALLog.d( "download completed");
		return bytesCopied;
	}

	public int copy(InputStream input, RandomAccessFile out) throws Exception {
		byte[] buffer = new byte[BUFFER_SIZE];

		BufferedInputStream in = new BufferedInputStream(input, BUFFER_SIZE);
		out.seek(out.length());

		int count = 0, n = 0;
		long errorBlockTimePreviousTime = -1, expireTime = 0;
		try {
			while (!interrupt) {
				try {
					n = in.read(buffer, 0, BUFFER_SIZE);
				} catch (Exception e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
					interrupt = true;
					httpErrorStausCode = ALHttpDownloadErrorCode.AL_HTTP_DOWNLOAD_TYPE_ERROR_NO_INTENET_CONNECTION;
					break;

				}
				if (n == -1) {
					break;
				}

				out.write(buffer, 0, n);

				count += n;
				if (!AndroidUtils.isNetworkConnected(mContext)) {
					interrupt = true;
					httpErrorStausCode = ALHttpDownloadErrorCode.AL_HTTP_DOWNLOAD_TYPE_ERROR_NO_INTENET_CONNECTION;
					break;
				}

				if (networkSpeed == 0) {
					if (errorBlockTimePreviousTime > 0) {
						expireTime = System.currentTimeMillis()
								- errorBlockTimePreviousTime;
						if (expireTime > AL_HTTP_TIME_OUT) {
							httpErrorStausCode = ALHttpDownloadErrorCode.AL_HTTP_DOWNLOAD_TYPE_ERROR_NO_INTENET_CONNECTION;
							interrupt = true;
						}
					} else {
						errorBlockTimePreviousTime = System.currentTimeMillis();
					}
				} else {
					expireTime = 0;
					errorBlockTimePreviousTime = -1;
				}
			}
		} finally {
			try {
				out.close();
				in.close();
			} catch (IOException e) {
				httpErrorStausCode = ALHttpDownloadErrorCode.AL_HTTP_DOWNLOAD_TYPE_ERROR_UNKONW;
			}
		}
		return count;
	}

	private String getFileNameFromUrl(String url) {
		// 通过 ‘？’ 和 ‘/’ 判断文件名
		int index = url.lastIndexOf('?');
		String filename;
		if (index > 1) {
			filename = url.substring(url.lastIndexOf('/') + 1, index);
		} else {
			filename = url.substring(url.lastIndexOf('/') + 1);
		}

		if (filename == null || "".equals(filename.trim())) {// 如果获取不到文件名称
			filename = UUID.randomUUID() + ".apk";// 默认取一个文件名
		}
		return filename;
	}

	private String getFileNameFromHeader(String disposition) {
		String name = null;
		if (!TextUtils.isEmpty(disposition)) {
			try {
				name = new String(disposition.getBytes("ISO-8859-1"), "utf8");

				if (name.contains("filename")) {
					name = name.substring(name.indexOf("\"") + 1,
							name.lastIndexOf("\""));
				}

			} catch (UnsupportedEncodingException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}
		ALLog.d( "文件名是: " + name);
		return name;
	}

	public enum ALHttpDownloadErrorCode {
		// 下载成功
		AL_HTTP_DOWNLOAD_TYPE_ERROR_NONE,
		// 未挂载外部存储器
		AL_HTTP_DOWNLOAD_TYPE_ERROR_NO_EXTENAL_STORAGE,
		// SD卡空间不足
		AL_HTTP_DOWNLOAD_TYPE_ERROR_EXTENAL_STORAGE_OUT_OF_RANGE,
		// 网络不可用
		AL_HTTP_DOWNLOAD_TYPE_ERROR_NO_INTENET_CONNECTION,
		// 超时
		AL_HTTP_DOWNLOAD_TYPE_ERROR_TIME_OUT,
		// IO异常
		AL_HTTP_DOWNLOAD_TYPE_ERROR_IO_EXCEPTION,
		// 文件异常
		AL_HTTP_DOWNLOAD_TYPE_ERROR_FILE_NOT_FOUND,
		// 未知错误
		AL_HTTP_DOWNLOAD_TYPE_ERROR_UNKONW,
	}

}
